#include <Sieve.hpp>
#include <iostream>
#include <Trie.hpp>
#include <sstream>
#include <Utility.hpp>
#include <cstdlib>
#include <ctime>
#include <GTrie.hpp>
#include <mpi.h>
#include <list>
#include <vector>

Sieve::Sieve(int rank, int np, int ext) : 
rank(rank), np(np), ext(ext) {
	// nothing
}

void Sieve::run() {

	int vtime;

	// Todos recebem o timestamp atual
	MPI::COMM_WORLD.Bcast(&vtime, 1, MPI_INT, 0);

	// Configura seed unica para random
	srand(vtime + rank);

	int bsz;
	
	// Recebe tamanho da trie de partes serializada
	MPI::COMM_WORLD.Bcast(&bsz, 1, MPI_INT, 0);

	// Recebe trie serializada
	char * buffer = new char[bsz+1];
	buffer[bsz] = 0;
	MPI::COMM_WORLD.Bcast(buffer, bsz, MPI_CHAR, 0);

	std::stringstream ss;
	ss << std::string(buffer);
	delete buffer;

	// Cria trie (deserializa)
	Trie * trie = new Trie();
	int myqty = trie->deserialize(ss);

	int tryc = 0;
	// Cria trie de pedacos encontrados (para enviar para os outros nos)
	Trie * foundTrie = new Trie();

	// Buffer para geracao aleatoria de partes
	char wbuffer[6];

	// Enquanto faltarem partes a serem encontradas e comunicacao
	// nao for devidamente encerrada
	for (int found = 0;; ) {

		// Se chegou a quantidade de tentativas limite antes de troca
		// de dados ou se nao ha mais palavras para serem encontradas,
		// porem comunicacao nao foi encerrada
		if (tryc == ext || found >= myqty) {
	
			// zera quantidade de tentativas
			tryc = 0;

			// Envia partes encontradas

			std::stringstream ss;
			foundTrie->serialize(ss);
			std::string str;
			ss >> str;
			int sz = str.size();
			buffer = new char[sz+1];

			buffer[sz] = 0;
			for (int i = 0; i < sz; i++)
				buffer[i] = str[i];

			int status = found < myqty;
			MPI::COMM_WORLD.Send(&status, 1, MPI_INT, 0, 1);

			MPI::COMM_WORLD.Send(&sz, 1, MPI_INT, 0, 1);
			MPI::COMM_WORLD.Send(buffer, sz, MPI_CHAR, 0, 1);
			delete buffer;

			// Recebe partes encontradas por todos os outros nos

			int bsz;
			MPI::COMM_WORLD.Bcast(&bsz, 1, MPI_INT, 0);
			
			buffer = new char[bsz+1];
			buffer[bsz] = 0;
			MPI::COMM_WORLD.Bcast(buffer, bsz, MPI_CHAR, 0);

			std::stringstream ss2;
			ss2 << std::string(buffer);
			delete buffer;
			foundTrie->deserialize(ss2);

			std::list<std::string> list;
			foundTrie->itemize(list);
			std::list<std::string>::iterator it, itEnd;
			it = list.begin();
			itEnd = list.end();
			

			// Marca como encontrado as partes que os outros encontraram

			int helped = 0;
			for (; it != itEnd; it++) {
				Trie * search = 
					trie->retrieve(it->c_str());
				if (search) {
					helped ++;
					found++;
					search->remove();
				}
			}


			delete foundTrie;
			foundTrie = new Trie();

			MPI::COMM_WORLD.Bcast(&status, 1, MPI_INT, 0);

			if (!status) break;
			else if (found >= myqty) continue;
		}

		// Gera parte aleatoria
		Utility::randomWord(wbuffer);

		// Tenta encontra-la
		Trie * search = 
			trie->retrieveUntil(wbuffer);
		
		// Incrementa contador de tentativas
		tryc++;
		// Caso tenha sido encontrada
		if (search) {
			// Incrementa contador de encontradas
			found++;
			// Remove da trie
			search->remove();
			// Insere na trie de partes encontradas
			foundTrie->insert(wbuffer);
		}
	}

	delete foundTrie;
	delete trie;

	MPI::COMM_WORLD.Barrier();

}

